#include "DiagramMan.h"
#include "DiagramFam.h"

#include "../util/hacky_compares.h"

#include <algorithm>
#include <stdexcept>
#include <QVariant>

using boost::shared_ptr;
using std::runtime_error;

DiagramMan::DiagramMan(QWidget* canvasParent)
:	_canvases(canvasParent),
	_ml(NULL)
{
	_canvases.setTabsClosable(true);
	connect(&_canvases, SIGNAL(tabCloseRequested(int)), SLOT(closeTab(int)));

	/* There is no way the tab could change before we leave the constructor:
	 * there are no tabs yet! */
	connect(&_canvases, SIGNAL(currentChanged(int)), SLOT(tabChanged(int)));
}

DiagramMan::~DiagramMan()
{
	/* Don't receive any signals from the tab manager while we're destructing!
	 * For example, tabChanged() was being called, effectively re-constructing
	 * _cFam after it was already destructed. Evil. :( */
	_canvases.disconnect(this);
}

MouseListener* DiagramMan::mouseListener()
{
	return _ml;
}

void DiagramMan::setMouseListener(MouseListener* ml)
{
	_ml = ml;
	active().canvas().setMouseListener(_ml);
}

QWidget* DiagramMan::display()
{
	/* Yes, give users complete access to our QWidget. The only access we need
	 * to prevent is adding or removing tabs, which is prevented since we return
	 * a QWidget and not a QTabWidget. We don't care if the user does any other
	 * operations (resize, etc.).
	 */
	return &_canvases;
}

DiagramFam& DiagramMan::active()
{
	DiagramFam* fam = _cFam.get();

	if (!fam)
		throw runtime_error("No current diagram!");

	return *fam;
}

void DiagramMan::add(UMLDiagram* d)
{
	// NULL parent since we're adding to a tab
	add(DiagramFamPtr(new DiagramFam(d, NULL)));
}

void DiagramMan::add(const QString& path)
{
	add(DiagramFamPtr(new DiagramFam(path, NULL)));
}

void DiagramMan::add(DiagramFamPtr fam)
{
	_diagrams.push_back(fam);

	int index = addCanvas(*fam);
	_canvases.setCurrentIndex(index);
}

void DiagramMan::closeActive()
{
	closeTab(_canvases.currentIndex());
}

void DiagramMan::closeTab(int index)
{
	qDebug("Closing tab %i", index);

	try
	{
		// get the ptr first since it requires that the tab exists
		diagrams_type::iterator ptr = lookupPtr(index);

		// TODO: need to save?
		_canvases.removeTab(index); // does not delete page widget

		_diagrams.erase(ptr);
	}
	catch (std::exception& ex)
	{
		qWarning("Really sorry, got exception when closing the tab. Internal state's probably all whacked now. Details: %s", ex.what());
	}
}

// TODO: move somewhere sensible
Q_DECLARE_METATYPE(DiagramFam*);

int DiagramMan::addCanvas(DiagramFam& fam)
{
	UMLCanvas& canvas = fam.canvas();
	canvas.setProperty("bumlet_dman_dfam", QVariant::fromValue(&fam));

	// Invariant: all tabs added are UMLCanvases.
	return _canvases.addTab(&canvas, fam.name());
}

void DiagramMan::tabChanged(int selected)
{
	qDebug("Changing tabs to %d.", selected);

	// Clear old canvas's mouse listener.
	if (_cFam.get())
		_cFam->canvas().setMouseListener(NULL);

	if (selected != -1) // new tab selected?
	{
		diagrams_type::iterator ptr = lookupPtr(selected);

		if (ptr != _diagrams.end())
		{
			_cFam = *ptr;

			// Set mouse listener for new canvas.
			_cFam->canvas().setMouseListener(_ml);
		}
		else
		{
			qCritical("Could not find shared_ptr for DiagramFam.");
			_cFam.reset();
		}
	}
	else
		_cFam.reset();

	// No matter what happened the current diagram _did_ change if we got here.
	emit activeChanged(_cFam.get());
}

DiagramMan::diagrams_type::iterator DiagramMan::lookupPtr(int index)
{
	// Don't store this pointer!
	QWidget* tab = _canvases.widget(index);

	if (!tab)
		throw runtime_error("Couldn't find QWidget, invalid tab index!");

	// Don't store this pointer!
	DiagramFam* dfam =
		tab->property("bumlet_dman_dfam").value<DiagramFam*>();

	if (!dfam)
		throw runtime_error("No DiagramFam associated with tab!");

	return find(_diagrams.begin(), _diagrams.end(), dfam);
}

